{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Django2.2\n",
    "\n",
    "**Python Web Framework**：<https://wiki.python.org/moin/WebFrameworks>\n",
    "\n",
    "先说句大实话，Web端我一直都是`Net技术站`的`MVC and WebAPI`，Python我一直都是用些数据相关的知识（爬虫、简单的数据分析等）Web这块只是会Flask，其他框架也都没怎么接触过，都说`Python`的`Django`是`建站神器`，有`自动生成后端管理页面`的功能，于是乎就接触了下`Django2.2`（目前最新版本）\n",
    "> 逆天点评：Net的MVC最擅长的就是（通过Model+View）`快速生成前端页面和对应的验证`，而Python的`Django`最擅长的就是（通过注册Model）`快速生成后台管理页面`。**这两个语言都是快速建站的常用编程语言**（项目 V1~V2 阶段）\n",
    "\n",
    "网上基本上都是Django1.x的教程，很多东西在2下都有点不适用，所以简单记录下我的学习笔记以及一些心得：\n",
    "> PS：ASP.Net MVC相关文章可以参考我16年写的文章：<https://www.cnblogs.com/dunitian/tag/MVC/>\n",
    "\n",
    "官方文档：<https://docs.djangoproject.com/zh-hans/2.2/releases/2.2/>\n",
    "\n",
    "## 1.环境\n",
    "\n",
    "### 1.虚拟环境\n",
    "\n",
    "这个之前的确没太大概念，我一直都是用Conda来管理不同版本的包，现在借助Python生态圈里的工具`virtualenv`和`virtualenvwapper`\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "---\n",
    "\n",
    "### 2.Django命令\n",
    "\n",
    "1.**创建一个空项目：`django-admin startproject 项目名称`**\n",
    "> PS：项目名不要以数字开头哦~\n",
    "\n",
    "```shell\n",
    "# 创建一个base_demo的项目\n",
    "django-admin startproject base_demo\n",
    "\n",
    "# 目录结构\n",
    "|-base_demo （文件夹）\n",
    "|---__init__.py（说明这个文件夹是一个Python包）\n",
    "|---settings.py（项目配置文件：创建应用|模块后进行配置）\n",
    "|---urls.py（URL路由配置）\n",
    "|---wsgi.py（遵循wsgi协议：web服务器和Django的交互入口）\n",
    "|-manage.py（项目管理文件，用来生成应用|模块）\n",
    "```\n",
    "\n",
    "2.**创建一个应用：`python manage.py startapp 应用名称`**\n",
    "> 项目中一个模块就是一个应用，eg：商品模块、订单模块等\n",
    "\n",
    "```shell\n",
    "# 创建一个用户模块\n",
    "python manage.py startapp users\n",
    "\n",
    "├─base_demo\n",
    "│    __init__.py\n",
    "│    settings.py\n",
    "│    urls.py\n",
    "│    wsgi.py\n",
    "├─manage.py（项目管理文件，用来生成应用|模块）\n",
    "│\n",
    "└─users（新建的模块|应用）\n",
    "│   │  __init__.py\n",
    "│   │  admin.py（后台管理相关）\n",
    "│   │  models.py（数据库相关模型）\n",
    "│   │  views.py（相当于MVC中的C，用来定义处理|视图函数）\n",
    "│   │  tests.py（写测试代码）\n",
    "│   │  apps.py：配置应用的元数据（可选）\n",
    "│   │\n",
    "│   └─migrations：数据迁移模块（根据Model内容生成的）\n",
    "│      __init__.py\n",
    "```\n",
    "\n",
    "**PS：记得在项目(`base_demo`)的settings.py注册一下应用模块哦~**\n",
    "\n",
    "```py\n",
    "INSTALLED_APPS = [\n",
    "    ......\n",
    "    'users', # 注册自己创建的模块|应用\n",
    "]\n",
    "```\n",
    "\n",
    "3.**运行项目：`python manage.py runserver`**\n",
    "> PS：指定端口：`python manage.py runserver 8080`\n",
    "\n",
    "## 2.MVT入门\n",
    "\n",
    "**大家都知道MVC（模型-视图-控制器），而Django的MVC叫做MVT（模型-视图-模版）**\n",
    "> PS：Django出来很早，名字是自己定义的，用法和理念是一样的\n",
    "\n",
    "### 2.1.M（模型）\n",
    "\n",
    "#### 2.1.1.类的定义\n",
    "\n",
    "- 1.**生成迁移文件：`python manage.py makemigrations`**\n",
    "    - PS：根据编写好的Model文件生成（模型里面可以不用定义ID属性）\n",
    "- 2.**执行迁移生成表：`python mange.py migrate`**\n",
    "    - PS：执行生成的迁移文件\n",
    "\n",
    "PS：类似于EF的`CodeFirst`，Django默认使用的是`sqlite`，更改数据库后面会说的\n",
    "\n",
    "先看个演示案例：\n",
    "\n",
    "**1.定义类文件**（会根据Code来生成DB）\n",
    "```py\n",
    "# users > models.py\n",
    "\n",
    "from django.db import models\n",
    "\n",
    "# 用户信息表\n",
    "class UserInfo(models.Model):\n",
    "    # 字符串类型，最大长度为20\n",
    "    name = models.CharField(max_length=20)\n",
    "    # 创建时间：日期类型\n",
    "    create_time = models.DateTimeField()\n",
    "    # 更新时间\n",
    "    update_time = models.DateTimeField()\n",
    "```\n",
    "\n",
    "**2. 生成数据库**\n",
    "```shell\n",
    "# 生成迁移文件\n",
    "> python manage.py makemigrations\n",
    "\n",
    "Migrations for 'userinfo':\n",
    "  userinfo\\migrations\\0001_initial.py\n",
    "    - Create model UserInfo\n",
    "\n",
    "# 执行迁移生成表\n",
    "> python manage.py migrate\n",
    "Operations to perform:\n",
    "  Apply all migrations: admin, auth, contenttypes, sessions, userinfo\n",
    "Running migrations:\n",
    "  Applying contenttypes.0001_initial... OK\n",
    "  Applying auth.0001_initial... OK\n",
    "  Applying admin.0001_initial... OK\n",
    "  Applying admin.0002_logentry_remove_auto_add... OK\n",
    "  Applying admin.0003_logentry_add_action_flag_choices... OK\n",
    "  Applying contenttypes.0002_remove_content_type_name... OK\n",
    "  Applying auth.0002_alter_permission_name_max_length... OK\n",
    "  Applying auth.0003_alter_user_email_max_length... OK\n",
    "  Applying auth.0004_alter_user_username_opts... OK\n",
    "  Applying auth.0005_alter_user_last_login_null... OK\n",
    "  Applying auth.0006_require_contenttypes_0002... OK\n",
    "  Applying auth.0007_alter_validators_add_error_messages... OK\n",
    "  Applying auth.0008_alter_user_username_max_length... OK\n",
    "  Applying auth.0009_alter_user_last_name_max_length... OK\n",
    "  Applying sessions.0001_initial... OK\n",
    "  Applying userinfo.0001_initial... OK\n",
    "```\n",
    "\n",
    "然后就自动生成对应的表了 ==> **`users_userinfo`**（应用名_模块中的类名）\n",
    "\n",
    "![1.sqlite.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190425132159094-1430139515.png)\n",
    "\n",
    "知识拓展：默认时间相关文章：<https://www.cnblogs.com/huchong/p/7895263.html>\n",
    "\n",
    "####  2.1.2.生成后台\n",
    "\n",
    "##### 1.配置本地化（设置后台管理页面是中文）\n",
    "\n",
    "主要就是修改`settings.py`文件的`语言`和`时区`（后台管理的语言和时间）\n",
    "\n",
    "```py\n",
    "# 使用中文（zh-hans可以这么记==>zh-汉'字'）\n",
    "LANGUAGE_CODE = 'zh-hans'\n",
    "\n",
    "# 设置中国时间\n",
    "TIME_ZONE = 'Asia/Shanghai'\n",
    "```\n",
    "\n",
    "##### 2.创建管理员\n",
    "\n",
    "**创建系统管理员：`python manage.py createsuperuser`**\n",
    "\n",
    "```shell\n",
    "python manage.py createsuperuser\n",
    "用户名 (leave blank to use 'win10'): dnt # 如果不填，默认是计算机用户名\n",
    "电子邮件地址: # 可以不设置\n",
    "Password:\n",
    "Password (again):\n",
    "Superuser created successfully.\n",
    "```\n",
    "\n",
    "**经验：如果忘记密码可以创建一个新管理员账号，然后把旧的删掉就行了**\n",
    "> PS：根据新password字段，修改下旧账号的password也可以\n",
    "\n",
    "课后拓展：<a href=\"https://blog.csdn.net/dsjakezhou/article/details/84319228\">修改django后台管理员密码</a>\n",
    "\n",
    "##### 3.后台管理页面\n",
    "\n",
    "主要就是**在admin中注册模型类**\n",
    "\n",
    "比如给之前创建的UserInfo类创建对应的管理页面：\n",
    "\n",
    "```py\n",
    "# base_demo > users > admin.py\n",
    "\n",
    "from users.models import UserInfo\n",
    "# from .models import UserInfo\n",
    "\n",
    "# 注册模型类（自动生成后台管理页面）\n",
    "admin.site.register(UserInfo) # .site别忘记\n",
    "```\n",
    "\n",
    "然后运行Django（`python manage.py runserver`），访问\"127.0.0.1:8080/admin\"，登录后就就可以管理了\n",
    "> PS：如果不想交admin，而是想在root下。那么可以修改项目的`urls.py`（后面会说）\n",
    "\n",
    "![2.admin.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190425132701645-2058965524.png)\n",
    "\n",
    "##### 4.制定化显示\n",
    "\n",
    "注册模型类就ok了，但是显示稍微有点不人性化，eg：\n",
    "\n",
    "![3.添加用户.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190425133243297-1194373344.png)\n",
    "\n",
    "列表页显示出来的标题是UserInfo对象，而我们平时一般显示用户名等信息\n",
    "![3.列表.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190425133430777-259130787.png)\n",
    "\n",
    "so ==> 可以自己改写下\n",
    "\n",
    "回顾下之前讲的：（程序是显示的`str(对象)`，那么我们重写魔方方法`__str__`即可改写显示了）\n",
    "\n",
    "```py\n",
    "# base_demo > users > models.py\n",
    "\n",
    "# 用户信息表\n",
    "class UserInfo(models.Model):\n",
    "    # 字符串类型，最大长度为20\n",
    "    name = models.CharField(max_length=20)\n",
    "    # 创建时间：日期类型\n",
    "    create_time = models.DateTimeField()\n",
    "    # 更新时间\n",
    "    update_time = models.DateTimeField()\n",
    "\n",
    "    def __str__(self):\n",
    "        \"\"\"为了后台管理页面的美化\"\"\"\n",
    "        return self.name\n",
    "```\n",
    "\n",
    "这时候再访问就美化了：(**不用重启Django**）\n",
    "\n",
    "![3.美化列表.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190425133840428-1396356170.png)\n",
    "\n",
    "Django就没有提供对应的方法？NoNoNo，我们继续看：\n",
    "\n",
    "```py\n",
    "# base_demo > users > admin.py\n",
    "\n",
    "from .models import UserInfo\n",
    "\n",
    "# 自定义模型管理页面\n",
    "class UserInfoAdmin(admin.ModelAdmin):\n",
    "    # 自定义管理页面的列表显示字段（和类属性相对应）\n",
    "    list_display = [\"id\", \"name\", \"create_time\", \"update_time\", \"datastatus\"]\n",
    "\n",
    "# 注册模型类和模型管理类（自动生成后台管理页面）\n",
    "admin.site.register(UserInfo, UserInfoAdmin)\n",
    "```\n",
    "\n",
    "其他什么都不用修改，后端管理列表的布局就更新了：\n",
    "> PS：设置Model的`verbose_name`就可以在后台显示中文，eg：`name = models.CharField(max_length=25, verbose_name=\"姓名\")`\n",
    "\n",
    "![3.自定义列表页面.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190425140525987-1801746184.png)\n",
    "\n",
    "还有更多个性化的内容后面会继续说的~\n",
    "\n",
    "### 2.3.V（视图）\n",
    "\n",
    "这个类比于MVC的C（控制器）\n",
    "> PS：这块比Net的MVC和Python的Flask要麻烦点，url地址要简单配置下映射关系（小意思，不花太多时间）\n",
    "\n",
    "这块刚接触稍微有点绕，所以我们借助图来看：\n",
    "\n",
    "**比如我们想访问users应用下的首页（`/users/index`）**\n",
    "\n",
    "#### 2.3.1.设置视图函数\n",
    "\n",
    "这个和定义控制器里面的方法没区别：\n",
    "> PS：函数必须含`request`（类比下类方法必须含的self）\n",
    "\n",
    "![4.users模块的视图.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190426151450564-1184327950.png)\n",
    "\n",
    "```py\n",
    "from django.http import HttpResponse\n",
    "\n",
    "# 1.定义视图函数\n",
    "# http://127.0.0.1:8000/users/index\n",
    "def index(request):\n",
    "    print(request)\n",
    "    # 响应浏览器请求（需要页面就去T拿，需要数据就去M找）\n",
    "    return HttpResponse('这是users应用模块的index页面哦~')\n",
    "```\n",
    "\n",
    "#### 2.3.2.配置路由\n",
    "\n",
    "因为我想要的地址是：`/users/index`，那么我在项目urls中也需要配置下访问`/users`的路由规则：\n",
    "> PS：我是防止以后模块多了管理麻烦，所以分开写，要是你只想在一个urls中配置也无妨\n",
    "\n",
    "![4.配置users路由.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190426152042117-501509824.png)\n",
    "\n",
    "```py\n",
    "# base_demo > urls.py\n",
    "\n",
    "from django.contrib import admin\n",
    "from django.urls import path, include\n",
    "\n",
    "# 项目urls配置文件\n",
    "urlpatterns = [\n",
    "    path('users/', include(\"users.urls\")),  # 配置项\n",
    "]\n",
    "```\n",
    "\n",
    "最后再贴一下users应用模块的匹配：\n",
    "\n",
    "![4.users模块下视图函数.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190426152347506-1754124621.png)\n",
    "\n",
    "```py\n",
    "# users > urls.py\n",
    "\n",
    "from django.urls import path\n",
    "from . import views\n",
    "\n",
    "# 2.进行url配置（建立url地址和视图的对应关系）\n",
    "urlpatterns = [\n",
    "    # /users/index ==> view的index处理函数\n",
    "    path('index', views.index),\n",
    "]\n",
    "```\n",
    "\n",
    "#### 2.3.3.url访问\n",
    "\n",
    "这时候你访问`127.0.0.1:8000/users/index`就可以了：\n",
    "\n",
    "![4.页面.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190426154047862-2133863089.png)\n",
    "\n",
    "简单说下这个过程：\n",
    "\n",
    "1. 先去项目的urls.py中进行匹配\n",
    "    - `path('users/', include(\"users.urls\")),  # 配置项`\n",
    "2. 发现只要是以`/users/`开头的都使用了`users`模块自己的`urls.py`来匹配\n",
    "    - `path('index', views.index),`\n",
    "3. 发现访问`/users/index`最后进入的视图函数是`index`\n",
    "4. 然后执行`def index(request):pass`里面的内容并返回\n",
    "\n",
    "### 2.4.T（模版）\n",
    "\n",
    "这个类比于MVC的V，我们来看个简单案例：\n",
    "\n",
    "#### 2.4.1.创建模版\n",
    "\n",
    "Django1.x版本需要配置下模版路径之类的，现在只要在对应模块下创建`templates`文件夹就可以直接访问了\n",
    "\n",
    "我们来定义一个list的模版：\n",
    "\n",
    "![5.定义模版.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190427143442068-1168076141.png)\n",
    "\n",
    "定义视图函数（类比定义控制器方法）\n",
    "\n",
    "![5.定义视图函数.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190427144206441-712238444.png)\n",
    "\n",
    "配置对应的路由：\n",
    "\n",
    "![5.配置对应的url.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190426215910537-278042489.png)\n",
    "\n",
    "然后就出效果了：\n",
    "\n",
    "![5.效果.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190426220018043-1314480230.png)\n",
    "\n",
    "如果把之前添加的数据删除掉，也会显示默认效果：\n",
    "\n",
    "![5.没有内容.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190427144518455-203345918.png)\n",
    "\n",
    "#### 2.4.2.指定模版\n",
    "\n",
    "也可以指定模版位置：（看个人习惯）\n",
    "\n",
    "打开项目`settings.py`文件，设置`TEMPLATES`的`DIRS`值，来指定默认模版路径：\n",
    "\n",
    "```py\n",
    "# Base_dir：当前项目的绝对路径\n",
    "# BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n",
    "\n",
    "# https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#templates\n",
    "TEMPLATES = [\n",
    "    {\n",
    "        ...\n",
    "        'DIRS': [os.path.join(BASE_DIR, 'templates')], # 模版文件的绝对路径\n",
    "        ...\n",
    "    },\n",
    "]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 扩展：使用MySQL数据库\n",
    "\n",
    "这篇详细流程可以查看之前写的文章：<a href=\"\" title=\"https://www.cnblogs.com/dotnetcrazy/p/10782441.html\" target=\"_blank\">稍微记录下Django2.2使用MariaDB和MySQL遇到的坑</a>\n",
    "\n",
    "这边简单过下即可：\n",
    "\n",
    "#### 1.创建数据库\n",
    "\n",
    "Django不会帮你创建数据库，需要自己创建，eg：`create database django charset=utf8;`\n",
    "\n",
    "#### 2.配置数据库\n",
    "\n",
    "我把对应的文档url也贴了：\n",
    "\n",
    "```py\n",
    "# https://docs.djangoproject.com/en/2.2/ref/settings/#databases\n",
    "DATABASES = {\n",
    "    'default': {\n",
    "        'ENGINE': 'django.db.backends.mysql',\n",
    "        'NAME': 'django',  # 使用哪个数据库\n",
    "        'USER': 'root',  # mysql的用户名\n",
    "        'PASSWORD': 'dntdnt',  # 用户名对应的密码\n",
    "        'HOST': '127.0.0.1',  # 数据库服务的ip地址\n",
    "        'PORT': 3306,  # 对应的端口\n",
    "        # https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-OPTIONS\n",
    "        'OPTIONS': {\n",
    "            # https://docs.djangoproject.com/zh-hans/2.2/ref/databases/#setting-sql-mode\n",
    "            # SQLMode可以看我之前写的文章：https://www.cnblogs.com/dotnetcrazy/p/10374091.html\n",
    "            'init_command': \"SET sql_mode='STRICT_TRANS_TABLES'\",  # 设置SQL_Model\n",
    "        },\n",
    "    }\n",
    "}\n",
    "```\n",
    "\n",
    "最小配置：\n",
    "\n",
    "![6.1.配置.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190428100909946-1594638238.png)\n",
    "\n",
    "项目init.py文件中配置：\n",
    "\n",
    "```py\n",
    "import pymysql\n",
    "\n",
    "# Django使用的MySQLdb对Python3支持力度不够，我们用PyMySQL来代替\n",
    "pymysql.install_as_MySQLdb()\n",
    "```\n",
    "\n",
    "图示：\n",
    "\n",
    "![6.2.init.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190428101008312-85199351.png)\n",
    "\n",
    "#### 3.解决干扰\n",
    "\n",
    "如果你的Django是最新的2.2,PyMySQL也是最新的0.93的话，你会发现Django会报错：\n",
    "> django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.\n",
    "\n",
    "这个是Django对MySQLdb版本的限制，我们使用的是PyMySQL，所以不用管它\n",
    "\n",
    "![6.3.错误1.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190428101746288-1699408933.png)\n",
    "\n",
    "再继续运行发现又冒了个错误：`AttributeError: 'str' object has no attribute 'decode'`\n",
    "\n",
    "这个就不能乱改了，所以先调试输出下：\n",
    "\n",
    "![6.4.调试.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190428101925741-707545580.png)\n",
    "\n",
    "发现是对字符串进行了decode解码操作：（一般对字符串进行编码，二进制进行解码）\n",
    "\n",
    "![6.5.bug.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190428101950198-1132075286.png)\n",
    "\n",
    "解决也很简单，改成encode即可\n",
    "\n",
    "![6.6.ok.png](https://img2018.cnblogs.com/blog/1127869/201904/1127869-20190428102045457-1853588866.png)\n",
    "\n",
    "然后就没问题了，之后创建新项目也不会有问题了\n",
    "\n",
    "### 扩展：避免命令忘记\n",
    "\n",
    "![7.偷懒.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190501115218198-957846270.png)\n",
    "\n",
    "![7.智能提示.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190501115811402-1737176844.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.MVT基础\n",
    "\n",
    "### 3.1.M基础\n",
    "\n",
    "#### 3.1.字段类型\n",
    "\n",
    "模型类的命名规则基本上和变量命名一致，然后添加一条：**不能含`__`**（双下划线）\n",
    "> PS：这个后面讲查询的时候你就秒懂了（`__`来间隔关键词）\n",
    "\n",
    "这边简单罗列下**常用字段类型**：**模型类数据库字段的定义：`属性名=models.字段类型(选项)`**\n",
    "\n",
    "| 字段类型                                                                | 备注                                                                         |\n",
    "| ----------------------------------------------------------------------- | ---------------------------------------------------------------------------- |\n",
    "| `AutoField`                                                             | **自增长的int类型**（Django默认会自动创建属性名为id的自动增长属性）          |\n",
    "| `BigAutoField`                                                          | **自增长的bigint类型**（Django默认会自动创建属性名为id的自动增长属性）       |\n",
    "| **`BooleanField`**                                                      | **布尔类型**，值为True或False                                                |\n",
    "| `NullBooleanField`                                                      | **可空布尔类型**，支持Null、True、False三种值                                |\n",
    "| **`CharField(max_length=最大长度)`**                                    | `varchar`**字符串**。参数max_length表示最大字符个数                          |\n",
    "| **`TextField`**                                                         | **大文本类型**，一般超过4000个字符时使用。                                   |\n",
    "| **`IntegerField`**                                                      | **整型**                                                                     |\n",
    "| `BigIntegerField`                                                       | **长整型**                                                                   |\n",
    "| **`DecimalField(max_digits=None, decimal_places=None)`**                | **十进制浮点数**，`max_digits`：总位数。`decimal_places`：小数占几位         |\n",
    "| `FloatField(max_digits=None, decimal_places=None)`                      | **浮点数**，`max_digits`：总位数。`decimal_places`：小数占几位               |\n",
    "| `DateField([auto_now=True] | [auto_now_add=True])`                      | **日期类型**，`auto_now_add`：自动设置创建时间，`auto_now`：自动设置修改时间 |\n",
    "| `TimeField([auto_now=True] | [auto_now_add=True])`                      | **时间类型**，参数同`DateField`                                              |\n",
    "| **`DateTimeField([auto_now=True] | [auto_now_add=True])`**              | **日期时间类型**，参数同`DateField`                                          |\n",
    "| **`UUIDField([primary_key=True,] default=uuid.uuid4, editable=False)`** | **UUID字段**                                                                 |\n",
    "\n",
    "后端常用字段：\n",
    "\n",
    "| 字段类型         | 备注                                                        |\n",
    "| ---------------- | ----------------------------------------------------------- |\n",
    "| `EmailField`     | `CharField`子类，专门用于**Email**的字段                    |\n",
    "| **`FileField`**  | **文件字段**                                                |\n",
    "| **`ImageField`** | **图片字段**，FileField子类，对内容进行校验以保证是有效图片 |\n",
    "\n",
    "#### 3.2.字段选项\n",
    "\n",
    "通过选项可以约束数据库字段，简单罗列几个常用的：\n",
    "\n",
    "| 字段选项       | 描述                                                     |\n",
    "| -------------- | -------------------------------------------------------- |\n",
    "| **`default`**  | `default=函数名|默认值`设置字段的**默认值**              |\n",
    "| `primary_key`  | `primary_key=True`设置**主键**(一般在`AutoField`中使用） |\n",
    "| `unique`       | `unique=True`设置**唯一键**                              |\n",
    "| **`db_index`** | `db_index=True`设置**索引**                              |\n",
    "| `db_column`    | `db_column='xx'`设置数据库的字段名（默认就是属性名）     |\n",
    "| `null`         | 字段是否可以为`null`（现在基本上都是不为`null`）         |\n",
    "\n",
    "和Django自动生成的后台管理相关的选项:（后台管理页面表单验证）\n",
    "\n",
    "| 管理页的表单选项   | 描述                                                                   |\n",
    "| ------------------ | ---------------------------------------------------------------------- |\n",
    "| **`blank`**        | `blank=True`设置表单验证中字段**是否可以为空**                         |\n",
    "| **`verbose_name`** | `verbose_name='xxx'`设置字段对应的**中文显示**（下划线会转换为空格）   |\n",
    "| **`help_text`**    | `help_text='xxx'`设置字段对应的**文档提示**（可以包含HTML）            |\n",
    "| `editable`         | `editable=False`设置字段**是否可编辑**（不可编辑就不显示在后台管理页） |\n",
    "| `validators`       | `validators=xxx`设置字段**验证**（<http://mrw.so/4LzsEq>）             |\n",
    "\n",
    "补充说明：\n",
    "\n",
    "1. 除非要覆盖默认的主键行为，否则不需要设置任何字段的`primary_key=True`（Django默认会创建`AutoField`来保存主键）\n",
    "2. ***`auto_now_add`和`auto_now`是互斥的，一个字段中只能设置一个，不能同时设置**\n",
    "3. 修改模型类时：如果添加的选项不影响表的结构，就不需要重新迁移\n",
    "    - 字段选项中`default`和`管理表单选项`(`blank`、`verbose_name`、`help_text`等)不影响表结构\n",
    "\n",
    "### ORM基础\n",
    "\n",
    "官方文档：<https://docs.djangoproject.com/zh-hans/2.2/ref/models/querysets/>\n",
    "\n",
    "`O`(objects)：类和对象，`R`(Relation)：关系型数据库，`M`(Mapping)：映射\n",
    "> PS：表 --> 类、每行数据 --> 对象、字段 --> 对象的属性\n",
    "\n",
    "进入命令模式：python manager.py shell\n",
    "\n",
    "增（有连接关系的情况）\n",
    "\n",
    "删（逻辑删除、删）\n",
    "\n",
    "改（内连接关联修改）\n",
    "\n",
    "查（总数、条件查询据、分页查询）\n",
    "\n",
    "表与表之间的关系（relation），主要有这三种：\n",
    "\n",
    "1. 一对一（one-to-one）：一种对象与另一种对象是一一对应关系\n",
    "    - eg：一个学生只能在一个班级。\n",
    "2. 一对多（one-to-many）： 一种对象可以属于另一种对象的多个实例\n",
    "    - eg：一张唱片包含多首歌。\n",
    "3. 多对多（many-to-many）：两种对象彼此都是\"一对多\"关系\n",
    "    - eg：比如一张唱片包含多首歌，同时一首歌可以属于多张唱片。\n",
    "\n",
    "\n",
    "#### 执行SQL语句\n",
    "\n",
    "官方文档：<https://docs.djangoproject.com/zh-hans/2.2/topics/db/sql/>\n",
    "\n",
    "#### 扩展：查看生成SQL\n",
    "\n",
    "课后拓展：<https://www.jianshu.com/p/b69a7321a115>\n",
    "\n",
    "#### 3.3.模型管理器类\n",
    "\n",
    "1. 改变查询的结果集\n",
    "    - eg：程序里面都是假删除，而默认的`all()`把那些假删除的数据也查询出来了\n",
    "2. 添加额外的方法\n",
    "    - eg：\n",
    "\n",
    "---\n",
    "\n",
    "### 3.2.V基础\n",
    "\n",
    "返回Json格式（配合Ajax）\n",
    "\n",
    "JsonResponse\n",
    "\n",
    "#### 3.2.1.\n",
    "\n",
    "#### URL路由\n",
    "\n",
    "#### 模版配置\n",
    "\n",
    "#### 重定向\n",
    "\n",
    "跳转到视图函数\n",
    "\n",
    "知识拓展：<https://www.cnblogs.com/attila/p/10420702.html>\n",
    "\n",
    "#### 动态生成URL\n",
    "\n",
    "模版中动态生成URL地址，类似于Net里面的`@Url.Action(\"Edit\",\"Home\",new {id=13})`\n",
    "> <https://docs.djangoproject.com/zh-hans/2.2/intro/tutorial03/#removing-hardcoded-urls-in-templates>\n",
    "\n",
    "#### 404和500页面\n",
    "\n",
    "调试信息关闭\n",
    "\n",
    "### 扩展：HTTPRequest\n",
    "\n",
    "**HTTPRequest的常用属性**：\n",
    "\n",
    "1. **`path`**：请求页面的完整路径（字符串）\n",
    "    - 不含域名和参数\n",
    "2. **`method`**：请求使用的HTTP方式（字符串）\n",
    "    - eg：`GET`、`POST`\n",
    "3. `encoding`：提交数据的编码方式（字符串）\n",
    "    - 如果为`None`：使用浏览器默认设置（PS：一般都是UTF-8）\n",
    "4. **`GET`**：包含get请求方式的所有参数（QueryDict类型，类似于Dict）\n",
    "5. **`POST`**：包含post请求方式的所有参数（类型同上）\n",
    "6. **`FILES`**：包含所有上传的文件（MultiValueDict类型，类似于Dict）\n",
    "7. **`COOKIES`**：以key-value形式包含所有cookie（Dict类型）\n",
    "8. **`session`**：状态保持使用（类似于Dict）\n",
    "\n",
    "#### 获取URL参数\n",
    "\n",
    "复选框：勾选on，不勾选None\n",
    "reques.POST.get(\"xxx\")\n",
    "\n",
    "#### Cookies\n",
    "\n",
    "基于域名来存储的，如果不指定过期时间则关闭浏览器就过期\n",
    "\n",
    "```py\n",
    "# set cookie\n",
    "response = Htpresponse对象（eg：JsonResponse，Response）\n",
    "# max_age：多少秒后过期\n",
    "# response.set_cookie(key,value,max_age=7*24*3600) # 1周过期\n",
    "# expires：过期时间，timedelta：时间间隔\n",
    "response.set_cookie(key,value,expires=datatime.datatime.now()+datatime.timedelta(days=7))\n",
    "return response;\n",
    "\n",
    "# get cookie\n",
    "value = request.COOKIES.get(key)\n",
    "```\n",
    "**PS：Cookie不管保存什么类型，取出来都是str字符串**\n",
    "\n",
    "知识拓展：<https://blog.csdn.net/cuishizun/article/details/81537316>\n",
    "\n",
    "#### Session\n",
    "\n",
    "Django的Session信息存储在`django_session`里面，可以根据sessionid（`session_key`）获取对应的`session_data`值\n",
    "\n",
    "![1.sessionid.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190510162155533-523050350.png)\n",
    "\n",
    "**PS：Session之所以依赖于Cookie，是因为Sessionid（唯一标识）存储在客户端，没有sessionid你怎么获取？**\n",
    "\n",
    "```py\n",
    "# 设置\n",
    "request.session[\"key\"] = value\n",
    "\n",
    "# 获取\n",
    "request.session.get(\"key\",默认值)\n",
    "\n",
    "# 删除指定session\n",
    "del request.session[\"key\"] # get(key) ？\n",
    "\n",
    "# 删除所有session的value\n",
    "# sessiondata里面只剩下了sessionid，而对于的value变成了{}\n",
    "request.session.clear()\n",
    "\n",
    "# 删除所有session（数据库内容全删了）\n",
    "request.session.flush()\n",
    "\n",
    "# 设置过期时间（默认过期时间是2周）\n",
    "request.session.set_expiry(不活动多少秒后失效)\n",
    "\n",
    "\n",
    "# PS：都是request里面的方法\n",
    "```\n",
    "**PS：Session保存什么类型，取出来就是什么类型**（Cookie取出来都是str）\n",
    "\n",
    "---\n",
    "\n",
    "#### 文件上传\n",
    "\n",
    "\n",
    "---\n",
    "\n",
    "### 3.3.T基础\n",
    "\n",
    "自定义404页面\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.Admin后台\n",
    "\n",
    "上面演示了一些简单的制定化知识点：<a href=\"#2.1.2.生成后台\">上节回顾</a>，现在简单归纳下`Django2.2`admin相关设置：\n",
    "\n",
    "### 4.1.修改后台管理页面的标题\n",
    "\n",
    "大致效果如下：\n",
    "\n",
    "![7.后台管理页面](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190504205940102-1264624779.png)\n",
    "\n",
    "在`admin.py`中设置`admin.site.site_header`和`admin.site.site_title`：\n",
    "\n",
    "![7.admin配置.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190504210121154-1717736601.png)\n",
    "\n",
    "### 4.2.修改app在Admin后台显示的名称\n",
    "\n",
    "大致效果如下：\n",
    "\n",
    "![7.大致效果.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190504210312122-731544995.png)\n",
    "\n",
    "先设置应用模块的中文名：**`verbose_name = 'xxx'`**\n",
    "\n",
    "![7.设置应用模块的中文名.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190504204034852-45175516.png)\n",
    "\n",
    "让配置生效：**`default_app_config = '应用名.apps.应用名Config'`**\n",
    "\n",
    "![7.1.配置生效.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190504204327495-1189085248.png)\n",
    "\n",
    "### 4.3.汉化显示应用子项\n",
    "\n",
    "大致效果如下：\n",
    "\n",
    "![7.2.效果.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190504210405885-72000235.png)\n",
    "\n",
    "在每个模型类中设置**`Meta`**类，并设置`verbose_name`和`verbose_name_plural`\n",
    "\n",
    "![7.2.汉化显示应用子项.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190504211207183-48476581.png)\n",
    "\n",
    "### 4.4.汉化表单字段和提示\n",
    "\n",
    "大致效果如下：\n",
    "\n",
    "![7.3.效果.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190504211720982-363096327.png)\n",
    "\n",
    "汉化表单的字段：**`verbose_name`**，显示字段提示：**`help_text`**\n",
    "\n",
    "![7.3.汉化表单字段.png](https://img2018.cnblogs.com/blog/1127869/201905/1127869-20190504211623827-1855804997.png)\n",
    "\n",
    "### 4.5.\n",
    "\n",
    "列表显示\n",
    "\n",
    "状态显示+字体颜色\n",
    "\n",
    "文件上传\n",
    "\n",
    "文本验证\n",
    "\n",
    "Tag过滤\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "apt install sqliteman"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
